package com.wrl.mmall.task;

import com.wrl.mmall.common.Const;
import com.wrl.mmall.common.RedissonManager;
import com.wrl.mmall.service.IOrderService;
import com.wrl.mmall.util.PropertiesUtil;
import com.wrl.mmall.util.RedisShardedPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 一个测试定时任务的类
 *
 * @author Wang Renlei
 * @Date 2019/6/12 0012
 */
@Slf4j
@Component
public class CloseOrderTask {

    @Autowired
    private IOrderService iOrderService;
    //@Autowired
    private RedissonManager redissonManager;

    /**
     * 第一个版本
     * 只适用于单机
     * 分布式的情况下会出现死锁
     */
    //@Scheduled(cron = "0 0/1 * * * ?")
    public void closeOrderTask1() {
        log.info("定时关单，开始");
        int hour = 2;
        iOrderService.closeOrder(hour);
        log.info("定时关单，结束");

    }

    /**
     * 第二个版本
     * 适用于分布式
     * 如果刚设置好key，却出现了卡死的情况，则会发生死锁
     */
    //@Scheduled(cron = "0 0/1 * * * ?")
    public void closeOrderTask2() {
        log.info("定时关单，开始");
        int lockTimeout = Integer.valueOf(PropertiesUtil.getProperty("redis.lock.timeout", "5000"));
        Long setnx = RedisShardedPoolUtil.setNx(Const.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
        //如果返回值是1，代表设置成功，获取锁
        if (setnx != null && setnx.intValue() == 1) {
            closeOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        } else {
            log.info("没有获取分布式锁：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        }
        log.info("定时关单，结束");
    }

    /**
     * 第三个版本
     * 适用于分布式，多重防死锁
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void closeOrderTask3() {
        log.info("定时关单，开始");
        int lockTimeout = Integer.valueOf(PropertiesUtil.getProperty("redis.lock.timeout", "5000"));
        //value是当前时间+2秒
        Long setnx = RedisShardedPoolUtil.setNx(Const.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
        if (setnx != null && setnx.intValue() == 1) {
            //如果返回值是1，代表设置成功，获取锁，开始定时关单的业务逻辑
            closeOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        } else {
            //没有获取到锁，继续判断时间戳，看是否可以重置并获取到锁
            String lockValueStr = RedisShardedPoolUtil.get(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
            //当前系统时间，大于
            if (lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)) {
                String getSetResult = RedisShardedPoolUtil.getSet(Const.RedisLock.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeout));
                //再次用当前时间戳 getset，返回给定key的旧值，->旧值判断，是否获取锁
                //当key不存在的时候，返回nil（redis官方文档的返回值） ->获取锁
                //这里set一个新的value值，获取旧的值
                if (getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr, getSetResult))) {
                    //真正获取到锁
                    closeOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
                } else {
                    log.info("咩有获取到分布式锁：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
                }
            } else {
                log.info("咩有获取到分布式锁：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
            }
        }
        log.info("定时关单，结束");
    }


    /**
     * 适用 redissonManager
     */
    //    @Scheduled(cron = "0 0/1 * * * ?")
    public void closeOrderTask4() {
        RLock lock = redissonManager.getRedisson().getLock(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        boolean getLock = false;
        try {
            if (getLock = lock.tryLock(0, 5, TimeUnit.SECONDS)) {
                log.info("Redisson获取分布式锁{}，ThreadName：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
                int hour = 2;
                iOrderService.closeOrder(hour);
            } else {
                log.info("Redisson没有获取分布式锁{}，ThreadName：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            log.error("Redisson分布式锁获取错误：{}" + e);
        } finally {
            if (!getLock) {
                return;
            }
            lock.unlock();
            log.error("Redisson释放分布式锁");
        }
    }

    private void closeOrder(String lockName) {
        //设置锁的有效期
        RedisShardedPoolUtil.expire(lockName, 5);  //有效期5秒
        log.info("获取{}，ThreadName：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
        int hour = 2;
        iOrderService.closeOrder(hour);
        //及时释放锁
        RedisShardedPoolUtil.del(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        log.info("释放{}，ThreadName：{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
    }

}
